using System;
using System.Collections.Generic;
using Sce.PlayStation.Core;
using Sce.PlayStation.Core.Input;

namespace Nyanimals
{
	public sealed class CObjectManager
	{
		private static readonly CObjectManager instance = new CObjectManager();
		private CPlayer _player;
		private List<CBaseObject> ActiveObjects; // master list that holds all objects
		private List<CBaseObject> objectsToErase; // holds objects that have been set to be removed        
        private List<CBaseFood> foodList; // hold all the foods
        private List<CNyanimal> nyanimalList;
        
        // for updating the camera
        Vector2 prevTouch;
        float cameraSpeed;
        Vector3 cameraRotation;
        Vector2 curRightStick;
        
        public List<CNyanimal> NyanList
        {
            get { return nyanimalList;}
        }
		
		private CObjectManager()
		{
			_player = null;
			ActiveObjects = new List<CBaseObject>();
			objectsToErase = new List<CBaseObject>();
            foodList = new List<CBaseFood>();
            nyanimalList = new List<CNyanimal>();
            
            // for the camera
            cameraRotation = Vector3.Zero;
            cameraSpeed = 100.0f;
		}
		public static CObjectManager Instance
		{
			get { return instance;}
		}
		public CPlayer Player
		{
			get 
			{
				if(_player == null)
				{
					throw new NullReferenceException("You need To Create a Player... Dumbass");
				}
				
				return _player;
			}
		}

		public CPlayer CreatePlayer (string szMeshName, string szAnimationName, float speed, Matrix4 worldMat)
		{
			if(_player != null)
			{
				throw new NullReferenceException("Player is already created... fuck face :)");
			}
			
			_player = new CPlayer();
   
            int meshIndex = CMeshManager.Instance.LoadMesh(szMeshName);
            
			_player.ContextIndex = CContextManager.Instance.AddAnimatedContext(meshIndex, worldMat);
			_player.Context.MeshIndex = meshIndex;
			_player.Context.AnimationIndex = CAnimationManager.Instance.AddAnimation(szAnimationName);
			
			_player.Context.Active = true;
			_player.Velocity = new Vector3(0, 0, 0);
			_player.Speed = speed;
            
            
            // split the passed in matrix
            _player.Pivot = worldMat.AxisW;
            worldMat.AxisW = Vector3.Zero;
            _player.LocalMatrix = worldMat;
						
			_player.Init();
			ActiveObjects.Add(_player);
			
			return _player;
		}
		public void CreatePlayer (string szMeshName, string szAnimationName, float speed, Matrix4 worldMat, out CPlayer newObject)
		{
			newObject = CreatePlayer(szMeshName, szAnimationName, speed, worldMat);
		}
		
		public CNyanimal CreateNyanimal(Matrix4 worldMat)
		{
            string szMeshName = "Charactors/Carrot/SR_Char_Carrot.SRM";
            string szAnimationName = "Charactors/Carrot/SR_Carrot_Animations.SRA";
            
			// get the actual context data
			int meshIndex = CMeshManager.Instance.LoadMesh(szMeshName);
			int animationIndex = CAnimationManager.Instance.AddAnimation(szAnimationName);
			int contextIndex = CContextManager.Instance.AddAnimatedContext(meshIndex, worldMat);
			
			// create hte nyanimal
			CNyanimal nyan = new CNyanimal();
			nyan.ContextIndex = contextIndex;
			nyan.Context.MeshIndex = meshIndex;
			nyan.Context.AnimationIndex = animationIndex;
			nyan.Context.Active = true;
            nyan.Speed = 5.0f;
            
            // split the passed in matrix
            nyan.Pivot = worldMat.AxisW;
            worldMat.AxisW = Vector3.Zero;
			nyan.LocalMatrix = worldMat;
           
            nyan.Init();
			
			ActiveObjects.Add(nyan);
            nyanimalList.Add(nyan);
			
			return nyan;
		}
		public void CreateNyanimal(Matrix4 worldMat, out CNyanimal outNyan)
		{
			outNyan = CreateNyanimal(worldMat);
		}
        
        public CBaseFood CreateFood(FoodTypes type, Matrix4 worldMat)
        {
            CBaseFood food;
            int meshIndex = -1;
            int contextIndex = -1;
            
            switch(type)
            {
            case FoodTypes.NyanBerry_Food:
                {
                    food = new CNyanBerry();
                    meshIndex = CMeshManager.Instance.LoadMesh("nyanberry.SRM");
                    contextIndex = CContextManager.Instance.AddStaticContext(meshIndex, worldMat);  
                } break;
            default:
                {
                    throw new InvalidOperationException("Not a valid food type!");
                } break;
            }
                               
            food.Init();
            food.ContextIndex = contextIndex;
            food.Context.MeshIndex = meshIndex;
            
            food.Position = worldMat.AxisW;
            worldMat.AxisW = Vector3.Zero;
            food.LocalMatrix = worldMat;
            
            ActiveObjects.Add(food);
            foodList.Add(food);
            return food;
        }
        public void CreateFood(FoodTypes type, Matrix4 worldMat, out CBaseFood outFood)
        {
            outFood = CreateFood(type, worldMat);
        }
		
        
        public CFoodBin AddFoodBin(Matrix4 worldMat)
        {
            CFoodBin bin = new CFoodBin();
            bin.Init();
            bin.WorldMatrix = worldMat;
            
            ActiveObjects.Add(bin);
            
            return bin;
        }
        public void AddFoodBin(Matrix4 worldMat, out CFoodBin newObject)
        {
            newObject = AddFoodBin(worldMat);
        }
        
		public void AddMovingObject(string szMeshName, float speed, Matrix4 worldMat, out CMovingObject newObject)
		{
            throw new NotImplementedException("Do not use this yet");
			// check the free list for any available objects
			bool gotUsed = false;
			newObject = new CMovingObject();
			newObject.ContextIndex = CContextManager.Instance.AddStaticContext(CMeshManager.Instance.LoadMesh(szMeshName), worldMat);
			newObject.Context.MeshIndex = CMeshManager.Instance.LoadMesh(szMeshName);
			newObject.Context.Active = true;
			newObject.Context.WorldMatrix = worldMat;
			newObject.WorldMatrix = worldMat;
			newObject.Velocity = new Vector3(0, 0, 0);
			newObject.Speed = speed;
			
			ActiveObjects.Add(newObject);			
		}
		
		public void RemoveObject(CBaseObject obj)
		{
            objectsToErase.Add(obj);
		}
        private void FlushObjects()
        {
            foreach(CBaseObject o in objectsToErase)
            {
                o.Shutdown();
                
                // remove from food list if food
                if(o.Type == ObjectTypes.Food_Object)
                {
                    foodList.Remove((CBaseFood)o);
                }
                
                ActiveObjects.Remove(o);
                CContextManager.Instance.RemoveContext(o.Context);
            }
            objectsToErase.Clear();
        }
        private void DeleteObject(CBaseObject obj)
        {            
             for(int objectIndex = 0; objectIndex < ActiveObjects.Count; ++objectIndex)
             {
                 if(ActiveObjects[objectIndex] == obj)
                 {
                     ActiveObjects.RemoveAt(objectIndex);
                     break;
                 }
             }
        }
        
        /// <summary>
        /// Finds the closest food.
        /// </summary>
        /// <returns>
        /// The closest food.  null if no food is found
        /// </returns>
        /// <param name='position'>
        /// Position of interest
        /// </param>
        /// <param name='type'>
        /// Type of food to find.  leave empty(the default) for all fruits
        /// </param>
        public CBaseFood FindClosestFood(Vector3 position, FoodTypes type = FoodTypes.Base_Food)
        {
            CBaseFood retFood = null;
            float shortestDist = 0.0f;
            foreach(CBaseFood food in foodList)
            {
                // our squared distence
                float myDist = CMath.Squared_Distence(position, food.Position);
                
                // shortest or havnt found yet
                if(myDist < shortestDist || retFood == null)
                {
                    // check if we are the correct type
                    if(food.Claim == false && food.Held == false && (food.FoodType == type || type == FoodTypes.Base_Food))
                    {
                        shortestDist = myDist;
                        retFood = food;
                    }
                }
            }
            
            return retFood;
        }
		
		public void Input()
		{
            curRightStick = CInput.Instance.RightAnalog;
		}
		
		public void Update(float deltaTime)
		{
			foreach(CBaseObject obj in ActiveObjects)
			{
				obj.Input();
				obj.Update(deltaTime);
				

            }
            
            FlushObjects();
            
            // collision checks            
            for(int objectIndex = 0; objectIndex < ActiveObjects.Count; ++objectIndex)
            {
                CBaseObject obj = ActiveObjects[objectIndex];
                 // collision checks             
                for(int objectIndex2 = 0; objectIndex2 < ActiveObjects.Count; ++objectIndex2)
                {
                     if(objectIndex == objectIndex2)
                         continue;
                        
                    CBaseObject obj2 = ActiveObjects[objectIndex2];
                    obj.HandleCollision(ref obj2);
                 }
            }
                     
            
		}
        
        public void UpdateCamAroundObject(CBaseObject obj, float deltaTime)
        {
            // update the camera
            CCamera cam = CCamera.Instance;
            CInput input = CInput.Instance;
            
            // the new rotation value
            if(input.Touching)
            {
                if(input.Status == TouchStatus.Down)
                {
                    prevTouch = input.TouchPoint;
                }
                else if(input.Status == TouchStatus.Move)
                {
                    Vector2 curTouch = input.TouchPoint;
                    Vector2 deltaTouch = curTouch - prevTouch;
                    prevTouch = curTouch;
                                        
                    cameraRotation.X = -deltaTouch.X * cameraSpeed;
                    cameraRotation.Y =  deltaTouch.Y * cameraSpeed;
                    
                    // Console.WriteLine("Touch Cam: " + cameraRotation.ToString());
                }
            }
            else
            {
                cameraRotation.X = -curRightStick.X * cameraSpeed * deltaTime;
                cameraRotation.Y = curRightStick.Y * cameraSpeed * deltaTime;
            }
            
            if(obj != null)
            {
                cam.Target = obj.WorldMatrix;
            }
            cam.IncreaseRotation(cameraRotation.Y, cameraRotation.X);
            cam.Update(deltaTime);
        }
	}
}

